import { join } from 'path'

import { Command, Option } from 'clipanion'
import inquirer, { Question } from 'inquirer'

import { SCM_BUILD_TYPE } from '../utils'

import { spawn } from './spawn'

const output = join(process.cwd(), 'output')
const resource = join(process.cwd(), 'output_resource')

const Projects = {
  'slardar/fe/sdk': {
    dir: 'packages/browser',
    build: buildSlardarSDK,
  },
  'slardar/fe/sdk_beta': {
    dir: 'packages/browser',
    build: buildSlardarSDK,
  },
  'slardar/fe/sdk_slardar_web': {
    dir: 'packages/web',
    build: buildSDKSlardarWeb,
  },
  // build for tce, the difference with slardar/fe/sdk_slardar_web is pluginPathPrefix
  'slardar/fe/sdk_web_for_tce': {
    dir: 'packages/web',
    build: buildSDKSlardarWebForNode,
  },
  'slardar/fe/sdk_server': {
    dir: 'packages/sdk-server',
    build: buildSlardarServer,
  },
  'toutiao/appmonitor/rangers_site_sdk': {
    dir: 'packages/rangers-site-sdk',
    build: buildRangersSiteSDK,
    region: 'rangers',
  },
  'toutiao/appmonitor/apmplus_web': {
    dir: 'packages/apmplus-web',
    build: buildApmPlusWeb,
    region: 'cn',
  },
  'toutiao/appmonitor/apmplus_mini_program': {
    dir: 'packages/apmplus-mini-program',
    build: buildApmPlusMiniProgram,
    region: 'cn',
  },
  'toutiao/appmonitor/sdk_diagnosis': {
    dir: 'packages/diagnosis',
    build: buildDiagnosis,
    region: 'cn',
  },
  'toutiao/appmonitor/rangers_mini_sdk': {
    dir: 'packages/rangers-mini-sdk',
    build: buildRangersMiniSDK,
    region: 'rangers',
  },
  'slardar/fe/sdk_hybrid': {
    dir: 'packages/hybrid',
    files: ['hybrid', 'ios_bridge', 'android_bridge'],
    build: buildSlardarHybridSDK,
  },
  'slardar/fe/sdk_lite': {
    dir: 'packages/slardar-lite',
    build: buildSlardarLiteSDK,
  },
}

async function buildSlardarServer(this: typeof Projects['slardar/fe/sdk_server']) {
  const { dir } = this
  await spawn('yarn build:esm')
  await spawn(`cp -R ${dir}/build/src/* ${output}`)
  await spawn(`cp -R ${dir}/idl ${output}`)
  await spawn(`cp -R ${dir}/bootstrap.sh ${output}`)
  await spawn(`cp -R ${dir}/settings.py ${output}`)
  await spawn(`cp -R ${dir}/package.json ${output}`)

  await spawn(`mkdir -p ${join(output, 'node_modules')}`)
  await spawn(`yarn workspace @slardar/sdk-server install --prod --modules-folder ${join(output, 'node_modules')}`)
}

async function buildSDKSlardarWeb(this: typeof Projects['slardar/fe/sdk_slardar_web']) {
  const { dir } = this

  const isProd = SCM_BUILD_TYPE === 'prod'
  await spawn('UPLOAD_CDN=1 NODE_ENV=production yarn cli bundle -p web')

  const regionFile = async (region: string) => {
    // SCM output
    await spawn(`cp ${dir}/${region}/browser.${region}.js ${output}/`)
    await spawn(`cp ${dir}/${region}/constants.${region}.js ${output}/`)
    // for CDN
    const sdkCDN = join(resource, region)
    const pluginsCDN = join(resource, region, 'plugins')
    await spawn(`mkdir -p ${pluginsCDN}`)

    // cp primary files
    const cpWebResources = async (_region: string) => {
      isProd && (await spawn(`cp ${dir}/${_region}/browser.${_region}.js ${sdkCDN}/`))
      await spawn(`cp ${dir}/${_region}/browser.*.${_region}.js ${sdkCDN}/`)
    }
    await cpWebResources(region)

    await spawn(`cp ${dir}/${region}/plugins/*.js ${pluginsCDN}`)

    // for euttp
    if (region === 'maliva') await cpWebResources('euttp')
  }

  await Promise.all(['cn', 'maliva', 'sg', 'oci'].map((r) => regionFile(r)))
  const uploadDebugFilesWithRegion = async (region: string) => {
    const sdkCDN = join(resource, region)
    const pluginsCDN = join(resource, region, 'plugins')

    await spawn(`cp ${dir}/${region}/browser.${region}.debug.js ${sdkCDN}/`)
    await spawn(`cp ${dir}/${region}/browser.*.${region}.debug.js ${sdkCDN}/`)
    await spawn(`cp ${dir}/${region}/plugins/*.debug.js ${pluginsCDN}`)
  }
  if (!isProd) {
    await spawn('yarn clean')
    await spawn(`UPLOAD_CDN=1 NODE_ENV=development yarn cli bundle -p web`)
    await Promise.all(['cn', 'maliva', 'sg', 'oci'].map((r) => uploadDebugFilesWithRegion(r)))
  }
}

async function buildSDKSlardarWebForNode(this: typeof Projects['slardar/fe/sdk_web_for_tce']) {
  const { dir } = this

  await spawn('NODE_ENV=production yarn cli bundle -p web')

  const regionFile = async (region: string) => {
    // SCM output
    await spawn(`cp ${dir}/${region}/browser.${region}.js ${output}/`)
    await spawn(`cp ${dir}/${region}/constants.${region}.js ${output}/`)
  }

  await Promise.all(['cn', 'maliva', 'india', 'sg', 'oci'].map((r) => regionFile(r)))
}

async function buildSlardarSDK(this: typeof Projects['slardar/fe/sdk']) {
  const { dir } = this

  await spawn('NODE_ENV=production yarn cli bundle -p browser')

  const regionFile = async (region: string) => {
    const plugins = join(resource, region, 'plugins')
    const sdkCDN = join(resource, region)
    await spawn(`cp ${dir}/${region}/browser.${region}.js ${output}/`)
    await spawn(`cp ${dir}/${region}/constants.${region}.js ${output}/`)
    await spawn(`mkdir -p ${plugins}`)

    SCM_BUILD_TYPE === 'prod' && (await spawn(`cp ${dir}/${region}/browser.${region}.js ${sdkCDN}/`))
    await spawn(`cp ${dir}/${region}/browser.*.${region}.js ${sdkCDN}/`)
    await spawn(`cp ${dir}/${region}/plugins/*.js ${plugins}`)
  }

  await Promise.all(['cn', 'maliva', 'india', 'sg', 'oci'].map((r) => regionFile(r)))
}

async function buildApmPlusWeb(this: typeof Projects['toutiao/appmonitor/apmplus_web']) {
  const { dir } = this
  await spawn('NODE_ENV=production yarn cli bundle -p apmplus-web')
  const regionFile = async (region: string) => {
    await spawn(`mkdir -p ${resource}/${region}`)
    await spawn(`cp ${dir}/cn/browser.${region}.js ${output}/`)
    SCM_BUILD_TYPE === 'prod' && (await spawn(`cp ${dir}/cn/browser.${region}.js ${resource}/${region}`))
    await spawn(`cp ${dir}/cn/browser.*.${region}.js ${resource}/${region}`)
    await spawn(`cp ${dir}/cn/bridge.ios.*.${region}.js  ${resource}/${region}`)
  }
  await Promise.all(['cn'].map((r) => regionFile(r)))
}

async function buildApmPlusMiniProgram(this: typeof Projects['toutiao/appmonitor/apmplus_mini_program']) {
  const { dir } = this
  await spawn('NODE_ENV=production yarn cli bundle -p apmplus-mini-program')
  const platforms = ['wx', 'tt', 'my', 'swan', 'lark', 'mars']
  const regionFile = async (region: string) => {
    for (const platform of platforms) {
      await spawn(`mkdir -p ${resource}/${region}`)
      await spawn(`cp ${dir}/cn/${platform}.${region}.cjs.js ${output}/`)
      SCM_BUILD_TYPE === 'prod' && (await spawn(`cp ${dir}/cn/${platform}.${region}.cjs.js ${resource}/${region}`))
      await spawn(`cp ${dir}/cn/${platform}.*.${region}.cjs.js ${resource}/${region}`)
    }
  }
  await Promise.all(['cn'].map((r) => regionFile(r)))
}

async function buildDiagnosis(this: typeof Projects['toutiao/appmonitor/sdk_diagnosis']) {
  const { dir } = this
  await spawn('NODE_ENV=production yarn cli bundle -p diagnosis')
  const regionFile = async (region: string) => {
    await spawn(`mkdir -p ${resource}/${region}`)
    await spawn(`cp ${dir}/cn/*.js ${output}/`)
    SCM_BUILD_TYPE === 'prod' && (await spawn(`cp ${dir}/cn/*.js ${resource}/${region}`))
    await spawn(`cp ${dir}/cn/*.js ${resource}/${region}`)
  }
  await Promise.all(['cn'].map((r) => regionFile(r)))
}

async function buildRangersSiteSDK(this: typeof Projects['toutiao/appmonitor/rangers_site_sdk']) {
  const { dir } = this
  await spawn('NODE_ENV=production yarn cli bundle -p rangers-site-sdk')
  const regionFile = async (region: string) => {
    await spawn(`mkdir -p ${resource}/${region}`)
    await spawn(`cp ${dir}/cn/browser.${region}.js ${output}/`)
    SCM_BUILD_TYPE === 'prod' && (await spawn(`cp ${dir}/cn/browser.${region}.js ${resource}/${region}`))
    await spawn(`cp ${dir}/cn/browser.*.${region}.js ${resource}/${region}`)
  }
  await Promise.all(['cn'].map((r) => regionFile(r)))
}

async function buildRangersMiniSDK(this: typeof Projects['toutiao/appmonitor/rangers_mini_sdk']) {
  const { dir } = this

  await spawn('NODE_ENV=production yarn cli bundle -p rangers-mini-sdk')

  const regionFile = async (region: string) => {
    await spawn(`mkdir -p ${resource}/${region}`)
    await spawn(`cp ${dir}/${region}/index.${region}.js ${output}/`)
    await spawn(`cp ${dir}/${region}/index.*.${region}.js ${resource}/${region}`)
  }
  await Promise.all(['cn'].map((r) => regionFile(r)))
}

async function buildSlardarHybridSDK(this: typeof Projects['slardar/fe/sdk_hybrid']) {
  const { dir, files } = this

  await spawn('NODE_ENV=production yarn cli bundle -p hybrid')

  const regionFile = async (region: string) => {
    async function copyRegionFiles(file: string) {
      await spawn(`cp ${dir}/${region}/${file}.${region}.js ${output}/`)
      await spawn(`cp ${dir}/${region}/${file}.*.${region}.js ${resource}/${region}`)
    }

    await spawn(`mkdir -p ${resource}/${region}`)

    for (const filename of files) {
      await copyRegionFiles(filename)
    }
  }

  await Promise.all(['cn'].map((r) => regionFile(r)))
}

const projectQuestion: Question = {
  type: 'list',
  name: 'Choose a project to build',
  choices: Object.keys(Projects),
  prefix: '🛠',
}

async function buildSlardarLiteSDK(this: typeof Projects['slardar/fe/sdk_lite']) {
  const { dir } = this

  await spawn('NODE_ENV=production yarn cli bundle -p slardar-lite')

  const regionFile = async (region: string) => {
    const plugins = join(resource, region, 'plugins')
    const sdkCDN = join(resource, region)
    await spawn(`mkdir -p ${plugins}`)

    const cpLiteResources = async (_region: string) => {
      await spawn(`cp ${dir}/${_region}/browser.lite.*.${_region}.js ${output}/`)
      await spawn(`cp ${dir}/${_region}/browser-ajax.lite.*.${_region}.js ${output}/`)
      await spawn(`cp ${dir}/${_region}/browser-fetch.lite.*.${_region}.js ${output}/`)
      await spawn(`cp ${dir}/${_region}/browser-nocookie.lite.*.${_region}.js ${output}/`)
      if (SCM_BUILD_TYPE === 'prod') {
        await spawn(`cp ${dir}/${_region}/browser.lite.${_region}.js ${sdkCDN}/`)
        await spawn(`cp ${dir}/${_region}/browser-ajax.lite.${_region}.js ${sdkCDN}/`)
        await spawn(`cp ${dir}/${_region}/browser-fetch.lite.${_region}.js ${sdkCDN}/`)
        await spawn(`cp ${dir}/${_region}/browser-nocookie.lite.${_region}.js ${sdkCDN}/`)
      }
      await spawn(`cp ${dir}/${_region}/browser.lite.*.${_region}.js ${sdkCDN}/`)
      await spawn(`cp ${dir}/${_region}/browser-ajax.lite.*.${_region}.js ${sdkCDN}/`)
      await spawn(`cp ${dir}/${_region}/browser-fetch.lite.*.${_region}.js ${sdkCDN}/`)
      await spawn(`cp ${dir}/${_region}/browser-nocookie.lite.*.${_region}.js ${sdkCDN}/`)
      await spawn(`cp ${dir}/${_region}/plugins/*.js ${plugins}`)
    }

    await cpLiteResources(region)

    if (region === 'maliva') await cpLiteResources('euttp')
  }

  await Promise.all(['cn', 'maliva', 'india', 'sg', 'oci'].map((r) => regionFile(r)))
}

export class SCMCommand extends Command {
  static paths = [[`scm`]]

  project: string = Option.String(`-p,--project`)!

  async execute() {
    await this.preBuild()
    return this.scmBuild()
  }

  private async scmBuild() {
    const projectName =
      process.env.BUILD_REPO_NAME ?? this.project ?? (await inquirer.prompt([projectQuestion]))[projectQuestion.name!]
    if (!Projects[projectName]) {
      throw new TypeError(`Unsupported project: "${projectName}"`)
    }
    await Projects[projectName].build()
  }

  private async preBuild() {
    await spawn(`rm -rf ${output}`)
    await spawn(`rm -rf ${resource}`)
    await spawn('rm -rf dist')
    await spawn(`mkdir -p ${output}`)
    await spawn(`mkdir -p ${resource}`)
  }
}
